package fu.capstone.hanoicityguide.common.offlinemapultility;

import java.io.File;
import java.util.List;

import org.mapsforge.core.graphics.Bitmap;
import org.mapsforge.core.graphics.Paint;
import org.mapsforge.core.graphics.Style;
import org.mapsforge.core.model.LatLong;
import org.mapsforge.map.android.graphics.AndroidGraphicFactory;
import org.mapsforge.map.android.layer.MyLocationOverlay;
import org.mapsforge.map.android.util.AndroidUtil;
import org.mapsforge.map.android.view.MapView;
import org.mapsforge.map.layer.cache.TileCache;
import org.mapsforge.map.layer.overlay.Marker;
import org.mapsforge.map.layer.overlay.Polyline;
import org.mapsforge.map.layer.renderer.TileRendererLayer;
import org.mapsforge.map.rendertheme.InternalRenderTheme;

import android.content.Context;
import android.graphics.Color;
import android.graphics.Path;
import android.graphics.Typeface;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationManager;
import android.os.AsyncTask;
import android.os.Environment;
import android.util.Log;
import android.view.Gravity;
import android.view.View;
import android.view.View.MeasureSpec;
import android.widget.TextView;
import android.widget.Toast;

import com.graphhopper.GHRequest;
import com.graphhopper.GHResponse;
import com.graphhopper.GraphHopper;
import com.graphhopper.util.Constants;
import com.graphhopper.util.PointList;
import com.graphhopper.util.StopWatch;

import fu.capstone.hanoicityguide.R;
import fu.capstone.hanoicityguide.business.model.GeoCoordinate;

public class OfflineMapUtil {

	private TileCache tileCache;
	private TileRendererLayer tileRendererLayer;
	private GraphHopper hopper;
	private volatile boolean prepareInProgress = false;
	private volatile boolean shortestPathRunning = false;
	private org.mapsforge.map.android.layer.MyLocationOverlay mylocationoverlay;
	private Context context;
	
	public OfflineMapUtil(Context context) {
		this.context = context;
	}

	public void setupofflinemap(org.mapsforge.map.view.MapView mapview,
			Context context) {
		// initiate the mapview
		try {
			mapview.getModel();
			((View) mapview).setClickable(true);
			((MapView) mapview).getMapScaleBar().setVisible(true);
			((MapView) mapview).setBuiltInZoomControls(true);
			((MapView) mapview).getMapZoomControls().setZoomLevelMin((byte) 10);
			((MapView) mapview).getMapZoomControls().setZoomLevelMax((byte) 20);

			// create tile cache
			tileCache = AndroidUtil.createTileCache(context, "fragment",
					mapview.getModel().displayModel.getTileSize(), 1f,
					mapview.getModel().frameBufferModel.getOverdrawFactor());

			mapview.getModel().mapViewPosition.setCenter(new LatLong(21.015336,
					105.834267));
			mapview.getModel().mapViewPosition.setZoomLevel((byte) 12);

			// create render
			tileRendererLayer = new TileRendererLayer(tileCache,
					mapview.getModel().mapViewPosition, false,
					AndroidGraphicFactory.INSTANCE);

			tileRendererLayer.setMapFile(new File(Environment
					.getExternalStorageDirectory() + "/vietnam-latest.osm-gh",
					"vietnam.map"));
			tileRendererLayer.setXmlRenderTheme(InternalRenderTheme.OSMARENDER);

			// start render
			mapview.getLayerManager().getLayers().add(tileRendererLayer);
		} catch (Exception e) {
		}
	}

	public LatLong getcurrentLocation(Context context, MapView map) {

		LatLong currentLocation = null;
		mylocationoverlay = new MyLocationOverlay(context,
				map.getModel().mapViewPosition, null);
		mylocationoverlay.enableMyLocation(true);

		try {
			String provider;
			LocationManager locManager = (LocationManager) context
					.getSystemService(Context.LOCATION_SERVICE);
			Criteria criteria = new Criteria();
			provider = locManager.getBestProvider(criteria, false);
			Location lastKnownLoc = locManager
					.getLastKnownLocation(LocationManager.GPS_PROVIDER);
			currentLocation = new LatLong(lastKnownLoc.getLatitude(),
					lastKnownLoc.getLongitude());
		} catch (Exception e) {
		}

		return currentLocation;
	}

	public Marker createMyLocationMarker(Context context, LatLong p,
			int resource) {
		Drawable drawable = context.getResources().getDrawable(resource);
		Bitmap bitmap = AndroidGraphicFactory.convertToBitmap(drawable);
		return new Marker(p, bitmap, -bitmap.getHeight(),
				-bitmap.getWidth() / 2);
	}

	public Marker createMarker(Context context, LatLong p, String title,
			String snippet) {
		// Drawable drawable = context.getResources().getDrawable(resource);
		// Bitmap bitmap = AndroidGraphicFactory.convertToBitmap(drawable);
		// return new Marker(p, bitmap, -bitmap.getHeight(),
		// -bitmap.getWidth() / 2);
		TextView bubbleView = new TextView(context);
		bubbleView.setGravity(Gravity.CENTER_HORIZONTAL);
		Drawable background = context.getResources().getDrawable(
				R.drawable.balloon_overlay_unfocused);
		bubbleView.setBackground(background);
		bubbleView.setMaxEms(20);
		bubbleView.setTextSize(12);
		bubbleView.setTextColor(Color.BLACK);
		bubbleView.setTypeface(null, Typeface.BOLD);
		bubbleView.setText(title + "\n" + snippet);
		Bitmap bm = viewToBitmap(context, bubbleView);
		Marker mk = new Marker(p, bm, 0, -bm.getHeight() / 2);
		bm.incrementRefCount();
		return mk;
	}

	public Polyline createPolyline(GHResponse response) {
		Paint paintStroke = AndroidGraphicFactory.INSTANCE.createPaint();
		paintStroke.setStyle(Style.STROKE);
		paintStroke.setColor(Color.BLUE);
		paintStroke.setDashPathEffect(new float[] { 25, 15 });
		paintStroke.setStrokeWidth(8);

		// TODO: new mapsforge version wants an mapsforge-paint, not an android
		// paint.
		// This doesn't seem to support transparceny
		// paintStroke.setAlpha(128);
		Polyline line = new Polyline(
				(org.mapsforge.core.graphics.Paint) paintStroke,
				AndroidGraphicFactory.INSTANCE);
		List<LatLong> geoPoints = line.getLatLongs();
		PointList tmp = response.getPoints();
		for (int i = 0; i < response.getPoints().getSize(); i++) {
			geoPoints.add(new LatLong(tmp.getLatitude(i), tmp.getLongitude(i)));
		}

		return line;
	}

	public void initHopper() {
		Log.v("InitHopper", "loading graph (" + Constants.VERSION + ") ... ");
		new GHAsyncTask<Void, Void, Path>() {
			protected Path saveDoInBackground(Void... v) throws Exception {
				GraphHopper tmpHopp = new GraphHopper().forMobile();
				tmpHopp.setCHShortcuts("fastest");
				tmpHopp.load(new File(
						Environment.getExternalStorageDirectory(),
						"vietnam-latest.osm-gh").getAbsolutePath());
				Log.v("InitHopper", "found graph "
						+ tmpHopp.getGraph().toString() + ", nodes:"
						+ tmpHopp.getGraph().getNodes());
				hopper = tmpHopp;
				return null;
			}

			protected void onPostExecute(Path o) {
				if (hasError()) {
					Log.v("InitHopper",
							"An error happend while creating graph:"
									+ getErrorMessage());
				} else {
					Log.v("InitHopper", "Finished loading graph.");
				}

				finishPrepare();
			}
		}.execute();
	}

	public void finishPrepare() {
		prepareInProgress = false;
	}

	public void calcPath(final double fromLat, final double fromLon,
			final double toLat, final double toLon, final MapView mapview) {
		try {
		Log.v("CalcPath", "calculating path ...");
		new AsyncTask<Void, Void, GHResponse>() {
			float time;

			protected GHResponse doInBackground(Void... v) {
				StopWatch sw = new StopWatch().start();
				GHRequest req = new GHRequest(fromLat, fromLon, toLat, toLon)
						.setAlgorithm("dijkstrabi")
						.putHint("instructions", false)
						.putHint("douglas.minprecision", 1);
				GHResponse resp = hopper.route(req);
				time = sw.stop().getSeconds();
				return resp;
			}

			protected void onPostExecute(GHResponse resp) {
				if (!resp.hasErrors()) {
					Log.v("CalcPath", "from:" + fromLat + "," + fromLon
							+ " to:" + toLat + "," + toLon
							+ " found path with distance:" + resp.getDistance()
							/ 1000f + ", nodes:" + resp.getPoints().getSize()
							+ ", time:" + time + " " + resp.getDebugInfo());
					Log.v("CalcPath",
							"The route is " + (int) (resp.getDistance() / 100)
									/ 10f + "km long, time:" + resp.getMillis()
									/ 60000f + "min, debug:" + time);
					Toast.makeText(context, "The route is " + (int) (resp.getDistance() / 100)
									/ 1f + "km long.", Toast.LENGTH_LONG).show();
					mapview.getLayerManager().getLayers()
							.add(createPolyline(resp));
					// mapView.redraw();
				} else {
					Log.v("CalcPath", "Error:" + resp.getErrors());
				}
				shortestPathRunning = false;
			}
		}.execute();
		}catch(Exception e) {
			Log.v("Offline map", "Calc path");
		}
	}

	public Bitmap viewToBitmap(Context c, View view) {
		view.measure(MeasureSpec.getSize(view.getMeasuredWidth()),
				MeasureSpec.getSize(view.getMeasuredHeight()));
		view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
		view.setDrawingCacheEnabled(true);
		Drawable drawable = new BitmapDrawable(c.getResources(),
				android.graphics.Bitmap.createBitmap(view.getDrawingCache()));
		view.setDrawingCacheEnabled(false);
		return AndroidGraphicFactory.convertToBitmap(drawable);
	}

	public LatLong convertGeoCoordinateToLatLong(GeoCoordinate geocoordinate) {
		LatLong l = new LatLong(geocoordinate.getLatitude(),
				geocoordinate.getLongitude());
		return l;
	}
}
